﻿using System;
using System.Collections.Generic;
using System.ComponentModel;

using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.XPath;
using System.Text.RegularExpressions;
using System.IO;

namespace AirProperties
{
    public partial class AirWizard : Form
    {
        //validation regex pattens based on validation requirements in the Descriptor.1.5.xsd file from the AIR SDK
        //minor tweak to FILEPATH_PATTERN to prevent forward slash (/) as last character (causes packaging to fail with error) 
        private const string ID_PATTERN = @"^[A-Za-z0-9\-\.]{1,212}$";
        private const string FILENAME_PATTERN = @"^[^\*""/:&<>\?\\\|\. ]$|^[^\*""/:&<>\?\\\| ][^\*""/:&<>\?\\\|]*[^\*""/:<>\?\\\|\. ]$";
        private const string FILEPATH_PATTERN = @"^[^\*""/:&<>\?\\\|\. ]$|^[^\*""/:&<>\?\\\| ][^\*"":&<>\?\\\|]*[^\*""/:<>\?\\\|\. ]$";
        private const string COORDINATE_PATTERN = @"^(-)?\d+$";
        private const string NUMBER_PATTERN = @"^[0-9]*$";
        private PluginMain pluginMain;
        private String propertiesFilePath;
        private Boolean isPropertiesLoaded;

        public Boolean IsPropertiesLoaded
        {
            get { return isPropertiesLoaded; }
        }

        public AirWizard(PluginMain pluginMain)
        {
            this.pluginMain = pluginMain;
            InitializeComponent();
            SetTitle(PluginCore.PluginBase.CurrentProject.Name);
            InitializeControls();

            if (this.pluginMain.Settings.SelectPropertiesFileOnOpen)
            {
                SelectAndLoadPropertiesFile();
            }
            else
            {
                propertiesFilePath = Path.GetDirectoryName(PluginCore.PluginBase.CurrentProject.ProjectPath);
                propertiesFilePath += @"\application.xml";
                LoadProperties(true);
            }
        }

        private void InitializeControls()
        {
            cmbSystemChrome.Items.Add(new ListItem("None", "none"));
            cmbSystemChrome.Items.Add(new ListItem("Standard", "standard"));

            openIconFileDialog.InitialDirectory = Path.GetDirectoryName(PluginCore.PluginBase.CurrentProject.ProjectPath);
            openIconFileDialog.Title = "Select Icon Image File";
            openPropertiesFileDialog.InitialDirectory = Path.GetDirectoryName(PluginCore.PluginBase.CurrentProject.ProjectPath);
            openPropertiesFileDialog.Title = "Select Application Descriptor File";
        }

        public void SetTitle(string projectName)
        {
            this.Text = projectName + " - AIR Application Properties";
        }

        private void LoadProperties(Boolean isDefault)
        {
            string contentProperty;
            string[] minSizeProperty;
            string[] maxSizeProperty;

            isPropertiesLoaded = false;

            if (File.Exists(propertiesFilePath))
            {
                if (PropertyManager.InitialiseProperties(propertiesFilePath))
                {
                    //Details tab
                    PropertyManager.GetProperty("id", ref tbID);
                    PropertyManager.GetProperty("name", ref tbName);
                    PropertyManager.GetProperty("version", ref tbVersion);
                    PropertyManager.GetProperty("description", ref tbDescription);
                    PropertyManager.GetProperty("copyright", ref tbCopyright);

                    //Installation tab
                    PropertyManager.GetProperty("filename", ref tbFileName);
                    PropertyManager.GetProperty("installFolder", ref tbInstallFolder);
                    PropertyManager.GetProperty("programMenuFolder", ref tbProgramMenuFolder);
                    PropertyManager.GetProperty("allowBrowserInvocation", ref cbBrowserInvocation);
                    PropertyManager.GetProperty("customUpdateUI", ref cbCustomUpdateUI);
                    PropertyManager.GetProperty("icon/image16x16", ref tbAppIcon16);
                    PropertyManager.GetProperty("icon/image32x32", ref tbAppIcon32);
                    PropertyManager.GetProperty("icon/image48x48", ref tbAppIcon48);
                    PropertyManager.GetProperty("icon/image128x128", ref tbAppIcon128);

                    //Initial Window tab
                    contentProperty = PropertyManager.GetProperty("initialWindow/content");
                    tbContent.Text = System.Web.HttpUtility.UrlDecode(contentProperty);
                    PropertyManager.GetProperty("initialWindow/title", ref tbTitle);
                    PropertyManager.GetProperty("initialWindow/systemChrome", ref cmbSystemChrome, 1);
                    PropertyManager.GetProperty("initialWindow/transparent", ref cbTransparent);
                    PropertyManager.GetProperty("initialWindow/visible", ref cbVisible);
                    PropertyManager.GetProperty("initialWindow/resizable", ref cbResizable);
                    PropertyManager.GetProperty("initialWindow/minimizable", ref cbMinimizable);
                    PropertyManager.GetProperty("initialWindow/maximizable", ref cbMaximizable);
                    PropertyManager.GetProperty("initialWindow/x", ref tbX);
                    PropertyManager.GetProperty("initialWindow/y", ref tbY);
                    PropertyManager.GetProperty("initialWindow/width", ref tbWidth);
                    PropertyManager.GetProperty("initialWindow/height", ref tbHeight);
                    minSizeProperty = PropertyManager.GetProperty("initialWindow/minSize").Split(' ');
                    if (minSizeProperty.Length == 2)
                    {
                        tbMinSizeX.Text = minSizeProperty[0];
                        tbMinSizeY.Text = minSizeProperty[1];
                    }
                    maxSizeProperty = PropertyManager.GetProperty("initialWindow/maxSize").Split(' ');
                    if (maxSizeProperty.Length == 2)
                    {
                        tbMaxSizeX.Text = maxSizeProperty[0];
                        tbMaxSizeY.Text = maxSizeProperty[1];
                    }
                    isPropertiesLoaded = true;
                }
                else
                {
                    MessageBox.Show(PropertyManager.LastException.Message);
                }
            }
            else
            {
                if (isDefault && MessageBox.Show("Unable to locate the default application descriptor file (application.xml).\n\nDo you want to select the application descriptor file manually?", "Application Descriptor File Not Found", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation) == DialogResult.Yes)
                {
                    SelectAndLoadPropertiesFile();
                }
            }
        }

        private void SaveProperties()
        {
            if (File.Exists(propertiesFilePath))
            {
                //Details tab
                PropertyManager.SetProperty("id", tbID);
                PropertyManager.SetProperty("name", tbName);
                PropertyManager.SetProperty("version", tbVersion);
                PropertyManager.SetProperty("description", tbDescription);
                PropertyManager.SetProperty("copyright", tbCopyright);

                //Installation tab
                PropertyManager.SetProperty("filename", tbFileName);
                PropertyManager.SetProperty("installFolder", tbInstallFolder);
                PropertyManager.SetProperty("programMenuFolder", tbProgramMenuFolder);
                PropertyManager.SetProperty("allowBrowserInvocation", cbBrowserInvocation);
                PropertyManager.SetProperty("customUpdateUI", cbCustomUpdateUI);
                PropertyManager.SetProperty("icon/image16x16", tbAppIcon16);
                PropertyManager.SetProperty("icon/image32x32", tbAppIcon32);
                PropertyManager.SetProperty("icon/image48x48", tbAppIcon48);
                PropertyManager.SetProperty("icon/image128x128", tbAppIcon128);

                //Initial Window tab
                PropertyManager.SetProperty("initialWindow/content", System.Web.HttpUtility.UrlPathEncode(tbContent.Text));
                PropertyManager.SetProperty("initialWindow/title", tbTitle);
                PropertyManager.SetProperty("initialWindow/systemChrome", cmbSystemChrome);
                PropertyManager.SetProperty("initialWindow/transparent", cbTransparent);
                PropertyManager.SetProperty("initialWindow/visible", cbVisible);
                PropertyManager.SetProperty("initialWindow/resizable", cbResizable);
                PropertyManager.SetProperty("initialWindow/minimizable", cbMinimizable);
                PropertyManager.SetProperty("initialWindow/maximizable", cbMaximizable);
                PropertyManager.SetProperty("initialWindow/x", tbX);
                PropertyManager.SetProperty("initialWindow/y", tbY);
                PropertyManager.SetProperty("initialWindow/width", tbWidth);
                PropertyManager.SetProperty("initialWindow/height", tbHeight);
                PropertyManager.SetProperty("initialWindow/minSize", (tbMinSizeX.Text + " " + tbMinSizeY.Text).Trim());
                PropertyManager.SetProperty("initialWindow/maxSize", (tbMaxSizeX.Text + " " + tbMaxSizeY.Text).Trim());

                PropertyManager.CommitProperties(propertiesFilePath);
            }
        }

        private void SelectAndLoadPropertiesFile()
        {
            if (openPropertiesFileDialog.ShowDialog() == DialogResult.OK)
            {
                propertiesFilePath = openPropertiesFileDialog.FileName;
                LoadProperties(false);
            }
        }

        private void UpdateAppIcon(string fileName, ref TextBox fileNameTextBox, Point dimensions)
        {

            string projectPath = Path.GetDirectoryName(PluginCore.PluginBase.CurrentProject.ProjectPath);
            string iconFolder = this.pluginMain.Settings.ProjectIconsFolder;
            string destinationPath = projectPath + @"\" + iconFolder;
            string destinationFileName;
            string packagedFileName = this.pluginMain.Settings.PackageIconsFolder;
            Bitmap img = new Bitmap(fileName);

            //first, check if the image is the correct size
            if (img.Width == dimensions.X && img.Height == dimensions.Y)
            {
                //now check if file is in path relative to project root folder
                //and if not, save to icons folder as specified by plugin settings
                if (!fileName.ToLower().StartsWith(projectPath.ToLower()))
                {

                    if (!Directory.Exists(destinationPath))
                        Directory.CreateDirectory(destinationPath);

                    if (pluginMain.Settings.UseUniformFilenames)
                    {
                        destinationFileName = tbFileName.Text + dimensions.X.ToString() + Path.GetExtension(fileName);

                    }
                    else if (pluginMain.Settings.RenameIconsWithSize)
                    {
                        destinationFileName = Path.GetFileNameWithoutExtension(fileName) + dimensions.X.ToString() + Path.GetExtension(fileName);
                    }
                    else
                    {
                        destinationFileName = Path.GetFileName(fileName);
                    }

                    File.Copy(fileName, destinationPath + @"\" + destinationFileName, true);

                    if (packagedFileName.Length > 1)
                    {
                        packagedFileName += @"/";
                    }

                    packagedFileName += destinationFileName;

                    fileNameTextBox.Text = packagedFileName;
                }
            }
            else
            {
                MessageBox.Show("The selected icon image must be " + dimensions.X.ToString() + " x " + dimensions.Y.ToString() + " pixels in size.", "Invalid Image Size", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        #region Event Handlers

        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btnOK_Click(object sender, EventArgs e)
        {

            if (ValidateChildren())
            {
                SaveProperties();
                Close();
            }
            else
            {
                MessageBox.Show("One or more properties contains an invalid value. Please update the invalid value(s).", "Invalid Property Value(s)", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        private void btnHelp_Click(object sender, EventArgs e)
        {
            System.Diagnostics.Process.Start("http://livedocs.adobe.com/flex/3/html/help.html?content=File_formats_1.html");
        }

        private void cmbSystemChrome_SelectedIndexChanged(object sender, EventArgs e)
        {
            ValidateSystemChrome();
        }

        private void btnAppIcon16_Click(object sender, EventArgs e)
        {
            if (openIconFileDialog.ShowDialog() == DialogResult.OK)
            {
                UpdateAppIcon(openIconFileDialog.FileName, ref tbAppIcon16, new Point(16, 16));
            }
        }

        private void btnAppIcon32_Click(object sender, EventArgs e)
        {
            if (openIconFileDialog.ShowDialog() == DialogResult.OK)
            {
                UpdateAppIcon(openIconFileDialog.FileName, ref tbAppIcon32, new Point(32, 32));
            }
        }

        private void btnAppIcon48_Click(object sender, EventArgs e)
        {
            if (openIconFileDialog.ShowDialog() == DialogResult.OK)
            {
                UpdateAppIcon(openIconFileDialog.FileName, ref tbAppIcon48, new Point(48, 48));
            }
        }

        private void btnAppIcon128_Click(object sender, EventArgs e)
        {
            if (openIconFileDialog.ShowDialog() == DialogResult.OK)
            {
                UpdateAppIcon(openIconFileDialog.FileName, ref tbAppIcon128, new Point(128, 128));
            }
        }

        #endregion

        #region Validation Event Handlers / Methods

        //Ensures that Transparent value is false and cannot 
        //be changed when System Chrome is Standard
        private void ValidateSystemChrome()
        {
            ListItem item;
            item = (ListItem)cmbSystemChrome.SelectedItem;

            if (item != null && item.Value == "standard")
            {
                cbTransparent.Checked = false;
                cbTransparent.Enabled = false;
            }
            else
            {
                cbTransparent.Enabled = true;
            }
        }

        private void tbID_Validating(object sender, CancelEventArgs e)
        {
            if (!Regex.IsMatch(tbID.Text, ID_PATTERN))
            {
                this.errorProvider1.SetError(tbID, "The ID property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbID, "");
        }

        private void tbName_Validating(object sender, CancelEventArgs e)
        {
            tbName.Text = tbName.Text.Trim();

            if (tbName.Text.Length > 0 && !Regex.IsMatch(tbName.Text, FILENAME_PATTERN))
            {
                this.errorProvider1.SetError(tbName, "The Name property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbName, "");
        }

        private void tbVersion_Validating(object sender, CancelEventArgs e)
        {
            if (tbVersion.Text.Length <= 0)
            {
                this.errorProvider1.SetError(tbVersion, "The Version property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbVersion, "");
        }

        private void tbFileName_Validating(object sender, CancelEventArgs e)
        {
            tbFileName.Text = tbFileName.Text.Trim();

            if (!Regex.IsMatch(tbFileName.Text, FILENAME_PATTERN))
            {
                this.errorProvider1.SetError(tbFileName, "The File Name property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbFileName, "");
        }

        private void tbInstallFolder_Validating(object sender, CancelEventArgs e)
        {
            tbInstallFolder.Text = tbInstallFolder.Text.Trim();

            if (tbInstallFolder.Text.Length > 0 && !Regex.IsMatch(tbInstallFolder.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbInstallFolder, "The Install Folder property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbInstallFolder, "");
        }

        private void tbProgramMenuFolder_Validating(object sender, CancelEventArgs e)
        {
            tbProgramMenuFolder.Text = tbProgramMenuFolder.Text.Trim();

            if (tbProgramMenuFolder.Text.Length > 0 && !Regex.IsMatch(tbProgramMenuFolder.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbProgramMenuFolder, "The Program Menu Folder property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbProgramMenuFolder, "");
        }

        private void tbAppIcon16_Validating(object sender, CancelEventArgs e)
        {
            tbAppIcon16.Text = tbAppIcon16.Text.Trim();

            if (tbAppIcon16.Text.Length > 0 && !Regex.IsMatch(tbAppIcon16.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbAppIcon16, "The Icon 16 x 16 property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbAppIcon16, "");
        }

        private void tbAppIcon32_Validating(object sender, CancelEventArgs e)
        {
            tbAppIcon32.Text = tbAppIcon32.Text.Trim();

            if (tbAppIcon32.Text.Length > 0 && !Regex.IsMatch(tbAppIcon32.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbAppIcon32, "The Icon 32 x 32 property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbAppIcon32, "");
        }

        private void tbAppIcon48_Validating(object sender, CancelEventArgs e)
        {
            tbAppIcon48.Text = tbAppIcon48.Text.Trim();

            if (tbAppIcon48.Text.Length > 0 && !Regex.IsMatch(tbAppIcon48.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbAppIcon48, "The Icon 48 x 48 property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbAppIcon48, "");
        }

        private void tbAppIcon128_Validating(object sender, CancelEventArgs e)
        {
            tbAppIcon128.Text = tbAppIcon128.Text.Trim();

            if (tbAppIcon128.Text.Length > 0 && !Regex.IsMatch(tbAppIcon128.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbAppIcon128, "The Icon 128 x 128 property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbAppIcon128, "");
        }

        private void tbContent_Validating(object sender, CancelEventArgs e)
        {
            tbContent.Text = tbContent.Text.Trim();

            if (!Regex.IsMatch(tbContent.Text, FILEPATH_PATTERN))
            {
                this.errorProvider1.SetError(tbContent, "The Content property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbContent, "");
        }

        private void tbX_Validating(object sender, CancelEventArgs e)
        {
            tbX.Text = tbX.Text.Trim();

            if (tbX.Text.Length > 0 && !Regex.IsMatch(tbX.Text, COORDINATE_PATTERN))
            {
                this.errorProvider1.SetError(tbX, "The Initial Location (X) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbX, "");
        }

        private void tbY_Validating(object sender, CancelEventArgs e)
        {
            tbY.Text = tbY.Text.Trim();

            if (tbY.Text.Length > 0 && !Regex.IsMatch(tbY.Text, COORDINATE_PATTERN))
            {
                this.errorProvider1.SetError(tbY, "The Initial Location (Y) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbY, "");
        }

        private void tbWidth_Validating(object sender, CancelEventArgs e)
        {
            tbWidth.Text = tbWidth.Text.Trim();

            if (tbWidth.Text.Length > 0 && !Regex.IsMatch(tbWidth.Text, NUMBER_PATTERN))
            {
                this.errorProvider1.SetError(tbWidth, "The Initial Size (X) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbWidth, "");
        }

        private void tbHeight_Validating(object sender, CancelEventArgs e)
        {
            tbHeight.Text = tbHeight.Text.Trim();

            if (tbHeight.Text.Length > 0 && !Regex.IsMatch(tbHeight.Text, NUMBER_PATTERN))
            {
                this.errorProvider1.SetError(tbHeight, "The Initial Size (Y) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbHeight, "");
        }

        private void tbMinSizeX_Validating(object sender, CancelEventArgs e)
        {
            bool isValid = true;
            tbMinSizeX.Text = tbMinSizeX.Text.Trim();

            if (tbMinSizeX.Text.Length > 0)
            {
                if (!Regex.IsMatch(tbMinSizeX.Text, NUMBER_PATTERN))
                    isValid = false;
                if (tbMaxSizeX.Text.Length > 0)
                {
                    if (Convert.ToInt32(tbMinSizeX.Text) > Convert.ToInt32(tbMaxSizeX.Text))
                        isValid = false;
                }
            }
            else if (tbMinSizeY.Text.Length > 0)
            {
                isValid = false;
            }

            if (!isValid)
            {
                this.errorProvider1.SetError(tbMinSizeX, "The Minimum Size (X) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbMinSizeX, "");
        }

        private void tbMinSizeY_Validating(object sender, CancelEventArgs e)
        {
            bool isValid = true;
            tbMinSizeY.Text = tbMinSizeY.Text.Trim();

            if (tbMinSizeY.Text.Length > 0)
            {
                if (!Regex.IsMatch(tbMinSizeY.Text, NUMBER_PATTERN))
                    isValid = false;
                if (tbMaxSizeY.Text.Length > 0)
                {
                    if (Convert.ToInt32(tbMinSizeY.Text) > Convert.ToInt32(tbMaxSizeY.Text))
                        isValid = false;
                }
            }
            else if (tbMinSizeX.Text.Length > 0)
            {
                isValid = false;
            }

            if (!isValid)
            {
                this.errorProvider1.SetError(tbMinSizeY, "The Minimum Size (Y) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbMinSizeY, "");
        }

        private void tbMaxSizeX_Validating(object sender, CancelEventArgs e)
        {
            bool isValid = true;
            tbMaxSizeX.Text = tbMaxSizeX.Text.Trim();

            if (tbMaxSizeX.Text.Length > 0)
            {
                if (!Regex.IsMatch(tbMaxSizeX.Text, NUMBER_PATTERN))
                    isValid = false;
                if (tbMinSizeX.Text.Length > 0)
                {
                    if (Convert.ToInt32(tbMaxSizeX.Text) < Convert.ToInt32(tbMinSizeX.Text))
                        isValid = false;
                }
            }
            else if (tbMaxSizeY.Text.Length > 0)
            {
                isValid = false;
            }

            if (!isValid)
            {
                this.errorProvider1.SetError(tbMaxSizeX, "The Maximum Size (X) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbMaxSizeX, "");
        }

        private void tbMaxSizeY_Validating(object sender, CancelEventArgs e)
        {
            bool isValid = true;
            tbMaxSizeY.Text = tbMaxSizeY.Text.Trim();

            if (tbMaxSizeY.Text.Length > 0)
            {
                if (!Regex.IsMatch(tbMaxSizeY.Text, NUMBER_PATTERN))
                    isValid = false;
                if (tbMinSizeY.Text.Length > 0)
                {
                    if (Convert.ToInt32(tbMaxSizeY.Text) < Convert.ToInt32(tbMinSizeY.Text))
                        isValid = false;
                }
            }
            else if (tbMaxSizeX.Text.Length > 0)
            {
                isValid = false;
            }

            if (!isValid)
            {
                this.errorProvider1.SetError(tbMaxSizeY, "The Maximum Size (Y) property is not valid.");
                e.Cancel = true;
            }
            else
                this.errorProvider1.SetError(tbMaxSizeY, "");
        }


        #endregion

    }
}
